import  socket
import  time
import  json
import os
import  struct
import  ssl
from config import packet_type,packet_attr
import logging
from PyQt5 import QtCore



#客户端的日志记录
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.DEBUG)
file_handler = logging.FileHandler('client.log')
file_handler.setLevel(level = logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)

class Client:
    #percent = 0
    def __init__(self,server_ip,server_port):
        self.server_ip = server_ip
        self.server_port = server_port

        #创建tcp连接,AF_INET用于Internet进程间通信
        self.socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        self.socket.connect((self.server_ip,self.server_port))

        #将socket打包成ssl socket
        self.client = ssl.wrap_socket(self.socket,keyfile='./ssl/privkey.pem',certfile='./ssl/certificate.pem',server_side=False)


    #用户注册
    def register(self,username,password):
        #发送注册请求数据报
        flag = False
        body = bytes(json.dumps(dict(user_name=username , user_password = password)).encode('utf-8'))
        header = struct.pack("!2I", packet_type.REGISTER_REQUEST,len(body))
        register_packet = header + body
        self.client.send(register_packet)
        logger.info('用户名：'+ username + ' 发起注册请求!')

        #接受服务器的反馈
        recv_header = self.client.recv(packet_attr.header_size)
        if recv_header:
            pkt_type ,body_size = struct.unpack('!2I',recv_header)
            assert pkt_type == packet_type.REGISTER_RESPONSE
            info_raw = self.client.recv(body_size)
            info_json = json.loads(info_raw)
            info = info_json['info']
            if info == "SUCCESS!" :
                print("注册成功！")
                logger.info('注册成功!')
                flag = True
            else:
                print("注册失败！"+ info)
                logger.info("注册失败!" + info)
                flag = False
        return flag

    def login(self,username,password):
        flag = False

        body = bytes(json.dumps(dict(user_name=username , user_password = password)).encode('utf-8'))
        header = struct.pack("!2I",packet_type.LOGIN_REQUEST,len(body))
        login_packet = header + body
        self.client.send(login_packet)
        #登陆后设置用户名，以便后续设置访问权限
        self.username = username
        logger.info('用户名:' + username + '发起登陆请求！')

        recv_header = self.client.recv(packet_attr.header_size)
        if recv_header:
            pkt_type ,body_size = struct.unpack('!2I',recv_header)
            assert pkt_type == packet_type.LOGIN_RESPONSE
            info_raw = self.client.recv(body_size)
            info_json = json.loads(info_raw)
            info = info_json['info']
            if info == "LOGIN SUCCESS!" :
                print("登陆成功！")
                logger.info('登陆成功!')
                flag = True
            else:
                print("登陆失败！"+ info)
                logger.info("登陆失败！"+ info)
                flag = False
        return flag

            
    #请求文件目录
    def showCatalog(self):
        username = self.username
        body = bytes(json.dumps(dict(user_name = username)).encode('utf-8'))
        header = struct.pack("!2I",packet_type.CATALOG_REQUEST,len(body))
        packet = header + body
        self.client.send(packet)
        logger.info('用户:'+username+'发起远程目录请求!')

        recv_header = self.client.recv(packet_attr.header_size)
        if recv_header:
            pkt_type,body_size = struct.unpack('!2I',recv_header)
            assert pkt_type == packet_type.CATALOG_RESPONSE
            file_list_raw = self.client.recv(body_size)
            file_list_json = json.loads(file_list_raw)
            file_list = file_list_json['file_list']
            print(file_list)
            logger.info('返回远程目录')
        return file_list

    #从服务器下载文件
    def download(self,filename):
        user_name = self.username
        #定义文件请求报文的包体，将来发送给服务器,告知要下载的文件名称
        body = bytes(json.dumps(dict(filename = filename,username = user_name)).encode('utf-8'))
        #报文头部的格式统一为两个4字节的整数，记录报文类型和包体大小
        header = struct.pack('!2I',packet_type.FILE_REQUEST,len(body))    
        packet = header+body
        self.client.send(packet)
        logger.info('客户端发起下载文件请求！')

        #接受服务器返回的报文头部
        recv_header = self.client.recv(packet_attr.header_size)
        if recv_header:
            #解析返回的报头
            pkt_type,body_size = struct.unpack('!2I',recv_header)
            assert(pkt_type == packet_type.FILE_METADATA)
            #接受文件的元数据
            meta_data_raw = self.client.recv(body_size)
            meta_data = json.loads(meta_data_raw)
            file_size = meta_data["filesize"]
            file_name = meta_data["filename"]
            print(file_name)
            print("下载文件"+file_name)
            logger.info("下载文件"+file_name)
            
            rest_size = file_size
            f = open(file_name,'wb')
            while rest_size > 0:
            #接收文件数据
                try:
                    content_header = self.client.recv(packet_attr.header_size)
                except Exception:
                    continue
                pkt_type , body_size = struct.unpack('!2I',content_header)
                assert(pkt_type == packet_type.FILE_CONTENT)
                file_data = self.client.recv(body_size)
                if file_data:
                        f.write(file_data)
                        rest_size -= body_size
            f.close()
            assert(rest_size == 0)
            logger.info('文件：'+file_name + '下载完成!')
            return True
            
    